package com.note.app.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.houbb.sensitive.word.core.SensitiveWordHelper;
import com.note.app.document.NoteContent;
import com.note.app.dto.NoteCreateDTO;
import com.note.app.dto.NoteDTO;
import com.note.app.dto.NoteUpdateDTO;
import com.note.app.dto.TagDTO;
import com.note.app.mapper.NoteMapper;
import com.note.app.mapper.ShareMapper;
import com.note.app.repository.NoteContentRepository;
import com.note.app.service.NoteService;
import com.note.app.service.TagService;
import com.note.common.domain.Note;
import com.note.common.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 笔记服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class NoteServiceImpl implements NoteService {

    private final NoteMapper noteMapper;
    private final NoteContentRepository noteContentRepository;
    private final TagService tagService;
    private final ShareMapper shareMapper;
    private final ApplicationEventPublisher applicationEventPublisher;

    @Value("${sensitive.word.threshold:10}")
    private int sensitiveWordThreshold;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createNote(Long userId, NoteCreateDTO dto) {
        // 1. 敏感词过滤
        String title = dto.getTitle();
        String content = dto.getContent();

        // 检查标题敏感词
        if (title != null && !title.isEmpty()) {
            List<String> titleSensitiveWords = SensitiveWordHelper.findAll(title);

            if (titleSensitiveWords.size() > sensitiveWordThreshold) {
                throw new BusinessException("标题包含过多违规内容，请修改后重试，违规词数量：" + titleSensitiveWords.size());
            }

            // 替换敏感词
            if (!titleSensitiveWords.isEmpty()) {
                dto.setTitle(SensitiveWordHelper.replace(title));
            }
        }

        // 检查内容敏感词
        if (content != null && !content.isEmpty()) {
            List<String> contentSensitiveWords = SensitiveWordHelper.findAll(content);

            if (contentSensitiveWords.size() > sensitiveWordThreshold) {
                throw new BusinessException("内容包含过多违规内容，请修改后重试，违规词数量：" + contentSensitiveWords.size());
            }

            // 替换敏感词
            if (!contentSensitiveWords.isEmpty()) {
                dto.setContent(SensitiveWordHelper.replace(content));
            }
        }

        // 2. 创建笔记基本信息
        Note note = new Note();
        note.setUserId(userId);
        note.setCollectionId(dto.getCollectionId());
        note.setTitle(dto.getTitle());
        note.setIsPublic(dto.getIsPublic());
        note.setIcon(dto.getIcon());
        note.setEmoji(dto.getEmoji());
        note.setViewCount(0);
        note.setVersion(1);
        note.setCreatedAt(LocalDateTime.now());
        note.setUpdatedAt(LocalDateTime.now());


        // 3. 保存笔记基本信息到MySQL
        noteMapper.insert(note);
        Long noteId = note.getId();

        // 4. 创建并保存笔记内容到MongoDB
        NoteContent noteContent = new NoteContent();
        noteContent.setNoteId(noteId);
        noteContent.setContent(dto.getContent());
        noteContent.setVersion(1);
        noteContent.setCreatedBy(userId);
        noteContent.setUpdatedBy(userId);
        noteContent.setCreatedAt(LocalDateTime.now());
        noteContent.setUpdatedAt(LocalDateTime.now());
        NoteContent savedContent = noteContentRepository.save(noteContent);

        // 5. 更新笔记的contentId
        Note noteToUpdate = new Note();
        noteToUpdate.setId(noteId);
        noteToUpdate.setContentId(savedContent.getId());
        noteMapper.updateById(noteToUpdate);

        // 6. 处理标签关联
        if (dto.getTagIds() != null && !dto.getTagIds().isEmpty()) {
            tagService.associateTagsWithNote(noteId, dto.getTagIds());
        }

        // 发布笔记创建事件
        applicationEventPublisher.publishEvent(note);

        return noteId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long updateNote(Long userId, NoteUpdateDTO dto) {
        // 1. 检查笔记是否存在并且用户有权限
        Note existingNote = noteMapper.selectNoteWithPermission(dto.getId(), userId);
        if (existingNote == null) {
            throw new BusinessException("笔记不存在或无权限操作");
        }

        // 2. 敏感词过滤
        String title = dto.getTitle();
        String content = dto.getContent();

        // 检查标题敏感词
        if (title != null && !title.isEmpty()) {
            List<String> titleSensitiveWords = SensitiveWordHelper.findAll(title);

            if (titleSensitiveWords.size() > sensitiveWordThreshold) {
                throw new BusinessException("标题包含过多违规内容，请修改后重试，违规词数量：" + titleSensitiveWords.size());
            }

            // 替换敏感词
            if (!titleSensitiveWords.isEmpty()) {
                dto.setTitle(SensitiveWordHelper.replace(title));
            }
        }

        // 检查内容敏感词
        if (content != null && !content.isEmpty()) {
            List<String> contentSensitiveWords = SensitiveWordHelper.findAll(content);

            if (contentSensitiveWords.size() > sensitiveWordThreshold) {
                throw new BusinessException("内容包含过多违规内容，请修改后重试，违规词数量：" + contentSensitiveWords.size());
            }

            // 替换敏感词
            if (!contentSensitiveWords.isEmpty()) {
                dto.setContent(SensitiveWordHelper.replace(content));
            }
        }

        // 3. 更新笔记基本信息
        Note noteToUpdate = new Note();
        noteToUpdate.setId(dto.getId());

        // 集合ID可以是null，表示移动到根目录
        noteToUpdate.setCollectionId(dto.getCollectionId());

        if (dto.getTitle() != null) {
            noteToUpdate.setTitle(dto.getTitle());
        }

        if (dto.getIsPublic() != null) {
            noteToUpdate.setIsPublic(dto.getIsPublic());
        }

        if (dto.getIcon() != null) {
            noteToUpdate.setIcon(dto.getIcon());
        }

        if (dto.getEmoji() != null) {
            noteToUpdate.setEmoji(dto.getEmoji());
        }

        noteToUpdate.setUpdatedAt(LocalDateTime.now());

        // 4. 处理内容更新
        if (dto.getContent() != null) {
            // 版本号递增
            int newVersion = existingNote.getVersion() + 1;
            noteToUpdate.setVersion(newVersion);

            // 先检查是否已存在相同版本
            Optional<NoteContent> existingContent =
                    noteContentRepository.findByNoteIdAndVersion(dto.getId(), newVersion);

            if (existingContent.isPresent()) {
                // 存在相同版本，删除它
                noteContentRepository.delete(existingContent.get());
                log.warn("删除重复版本内容: noteId={}, version={}", dto.getId(), newVersion);
            }

            // 保存新版本内容
            NoteContent noteContent = new NoteContent();
            noteContent.setNoteId(dto.getId());
            noteContent.setContent(dto.getContent());
            noteContent.setVersion(newVersion);
            noteContent.setCreatedBy(userId);
            noteContent.setUpdatedBy(userId);
            noteContent.setCreatedAt(LocalDateTime.now());
            noteContent.setUpdatedAt(LocalDateTime.now());
            NoteContent savedContent = noteContentRepository.save(noteContent);

            // 打印更新的内容
            log.info("更新的内容: {}", savedContent.toString());
            // 更新contentId指向最新内容
            noteToUpdate.setContentId(savedContent.getId());
        }

        // 5. 更新MySQL中的笔记信息
        noteMapper.updateById(noteToUpdate);

        // 6. 更新标签关联
        if (dto.getTagIds() != null) {
            // 先删除原有标签关联
            tagService.removeTagsFromNote(dto.getId());

            // 添加新的标签关联
            if (!dto.getTagIds().isEmpty()) {
                tagService.associateTagsWithNote(dto.getId(), dto.getTagIds());
            }
        }

        // 发布笔记更新事件
        applicationEventPublisher.publishEvent(noteToUpdate);

        return dto.getId();
    }

    @Override
    public NoteDTO getNoteDetail(Long userId, Long noteId) {
        // 1. 获取笔记信息，包含权限检查
        Note note = noteMapper.selectNoteWithPermission(noteId, userId);
        if (note == null) {
            throw new BusinessException("笔记不存在或无权限查看");
        }

        // 2. 转换为DTO
        NoteDTO noteDTO = convertToDTO(note);

        // 3. 获取笔记内容
        Optional<NoteContent> contentOpt = noteContentRepository.findById(note.getContentId());
        if (contentOpt.isPresent()) {
            noteDTO.setContent(contentOpt.get().getContent());
        }

        // 4. 获取标签列表
        List<TagDTO> tags = tagService.getTagsByNoteId(noteId);
        noteDTO.setTags(tags);

        // 5. 填充分享状态信息
        int shareCount = shareMapper.countActiveSharesByNoteId(noteId);
        noteDTO.setShareCount(shareCount);
        noteDTO.setIsShared(shareCount > 0);

        // 6. 获取最新的分享ID
        if (shareCount > 0) {
            Long shareId = shareMapper.getLatestShareIdByNoteId(noteId);
            noteDTO.setShareId(shareId);
        }

        return noteDTO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteNote(Long userId, Long noteId) {
        // 1. 检查笔记是否存在并且用户有权限
        Note note = noteMapper.selectNoteWithPermission(noteId, userId);
        if (note == null) {
            throw new BusinessException("笔记不存在或无权限操作");
        }

        // 2. 删除标签关联
        tagService.removeTagsFromNote(noteId);

        // 3. 删除MongoDB中的内容
        noteContentRepository.deleteByNoteId(noteId);

        // 4. 删除MySQL中的笔记记录
        boolean result = noteMapper.deleteById(noteId) > 0;

        // 发布笔记删除事件
        applicationEventPublisher.publishEvent(noteId);

        return result;
    }

    @Override
    public Page<NoteDTO> getUserNotes(Long userId, Long collectionId, long current, long size) {
        // 1. 分页查询用户笔记
        Page<Note> page = new Page<>(current, size);
        IPage<Note> notePage = noteMapper.selectPageByUserId(page, userId, collectionId);

        // 2. 转换结果
        Page<NoteDTO> result = new Page<>(notePage.getCurrent(), notePage.getSize(), notePage.getTotal());
        List<NoteDTO> records = notePage.getRecords().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        // 3. 批量获取笔记关联的标签
        for (NoteDTO noteDTO : records) {
            noteDTO.setTags(tagService.getTagsByNoteId(noteDTO.getId()));

            // 4. 填充分享状态信息
            int shareCount = shareMapper.countActiveSharesByNoteId(noteDTO.getId());
            noteDTO.setShareCount(shareCount);
            noteDTO.setIsShared(shareCount > 0);

            // 5. 获取最新的分享ID
            if (shareCount > 0) {
                Long shareId = shareMapper.getLatestShareIdByNoteId(noteDTO.getId());
                noteDTO.setShareId(shareId);
            }
        }

        result.setRecords(records);
        return result;
    }

    @Override
    public Page<NoteDTO> getPublicNotes(long current, long size) {
        // 1. 分页查询公开笔记
        Page<Note> page = new Page<>(current, size);
        IPage<Note> notePage = noteMapper.selectPublicNotePage(page);

        // 2. 转换结果
        Page<NoteDTO> result = new Page<>(notePage.getCurrent(), notePage.getSize(), notePage.getTotal());
        List<NoteDTO> records = notePage.getRecords().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());

        // 3. 批量获取笔记关联的标签
        for (NoteDTO noteDTO : records) {
            noteDTO.setTags(tagService.getTagsByNoteId(noteDTO.getId()));

            // 4. 填充分享状态信息
            int shareCount = shareMapper.countActiveSharesByNoteId(noteDTO.getId());
            noteDTO.setShareCount(shareCount);
            noteDTO.setIsShared(shareCount > 0);

            // 5. 获取最新的分享ID
            if (shareCount > 0) {
                Long shareId = shareMapper.getLatestShareIdByNoteId(noteDTO.getId());
                noteDTO.setShareId(shareId);
            }
        }

        result.setRecords(records);
        return result;
    }

    @Override
    public void incrementViewCount(Long noteId) {
        // TODO: 使用Redis实现计数器，避免频繁更新MySQL
        Note note = noteMapper.selectById(noteId);
        if (note != null) {
            Note noteToUpdate = new Note();
            noteToUpdate.setId(noteId);
            noteToUpdate.setViewCount(note.getViewCount() + 1);
            noteMapper.updateById(noteToUpdate);
        }
    }

    @Override
    public List<Integer> getNoteHistoryVersions(Long userId, Long noteId) {
        // 1. 检查笔记是否存在并且用户有权限
        Note note = noteMapper.selectNoteWithPermission(noteId, userId);
        if (note == null) {
            throw new BusinessException("笔记不存在或无权限查看");
        }

        // 2. 查询所有历史版本
        List<NoteContent> contents = noteContentRepository.findByNoteIdOrderByVersionDesc(noteId);

        // 3. 提取版本号
        return contents.stream()
                .map(NoteContent::getVersion)
                .collect(Collectors.toList());
    }

    @Override
    public NoteDTO getNoteByVersion(Long userId, Long noteId, Integer version) {
        // 1. 检查笔记是否存在并且用户有权限
        Note note = noteMapper.selectNoteWithPermission(noteId, userId);
        if (note == null) {
            throw new BusinessException("笔记不存在或无权限查看");
        }

        // 2. 查询指定版本的内容
        Optional<NoteContent> contentOpt = noteContentRepository.findByNoteIdAndVersion(noteId, version);
        if (!contentOpt.isPresent()) {
            throw new BusinessException("指定版本的笔记内容不存在");
        }

        // 3. 转换为DTO
        NoteDTO noteDTO = convertToDTO(note);
        noteDTO.setContent(contentOpt.get().getContent());
        noteDTO.setVersion(version);

        // 4. 获取标签列表
        List<TagDTO> tags = tagService.getTagsByNoteId(noteId);
        noteDTO.setTags(tags);

        return noteDTO;
    }

    /**
     * 将Note实体转换为NoteDTO
     */
    private NoteDTO convertToDTO(Note note) {
        NoteDTO dto = new NoteDTO();
        BeanUtils.copyProperties(note, dto);
        // 不加载内容，因为内容较大
        dto.setContent(null);
        dto.setTags(new ArrayList<>());
        // 初始化分享信息
        dto.setIsShared(false);
        dto.setShareCount(0);
        return dto;
    }
}